package com.ruoyi.common.utils;

import cn.hutool.core.util.StrUtil;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.FileInputStream;
import java.util.*;
import java.util.stream.Stream;

/**
 * @Author qiqi
 * @Date 2022/6/7 12:23
 * @Version 1.0
 */
public class DocxUtil {
    public static  Logger logger = LoggerFactory.getLogger(DocxUtil.class);
    /**
     * 水印参数
     */
    private static final String fontColor = "#D3D3D3"; // 字体颜色


    /**
     * 艺术字水印参数
     */
    private static final String fontName = "华文行楷"; // word字体
    private static final String fontSize = "0.5pt"; // 字体大小
    private static final int widthPerWord = 10; // 一个字平均长度，单位pt，用于：计算文本占用的长度（文本总个数*单字长度）
    private static final String styleRotation = "-45"; // 文本旋转角度
    /**
     * 因为docx为xml格式的结构，一下为docx中定义的部分常量引用
     **/
    public static final String RUN_NODE_NAME = "w:r";
    public static final String TEXT_NODE_NAME = "w:t";
    public static final String BOOKMARK_START_TAG = "w:bookmarkStart";
    public static final String BOOKMARK_END_TAG = "w:bookmarkEnd";
    public static final String BOOKMARK_ID_ATTR_ID = "w:id";
    public static final String NODENAME_BODY = "w:body";
    public static final String NODENAME_PARAGRAPH = "w:p";
    public static final String BOOKMARK_ID_ATTR_NAME = "w:name";
    public static final String STYLE_NODE_NAME = "w:rPr";
    public static final String PARAGRAPH_PROPERTIES_NAME = "w:pPr";


    //赋值静态成员变量正例
    private static Map<String, String> dataMap = new HashMap<String, String>();
    static {
        dataMap.put("签名", "签名");
    }

    /**
     * 将指定的字符串重复repeats次.
     * @param pattern 字符串
     * @param repeats 重复次数
     * @return 生成的字符串
     */
    private static String repeatString(String pattern, int repeats) {
        StringBuilder buffer = new StringBuilder(pattern.length() * repeats);
        Stream.generate(() -> pattern).limit(repeats).forEach(buffer::append);
        return new String(buffer);
    }



    /**
     * 构建Shape的样式参数
     * @param customText 水印文本
     * @return
     */
    private static String getShapeStyle(String customText, String styleTop) {
        StringBuilder sb = new StringBuilder();
        sb.append("position: ").append("absolute"); // 文本path绘制的定位方式
        sb.append(";width: ").append(customText.length() * widthPerWord).append("pt"); // 计算文本占用的长度（文本总个数*单字长度）
        sb.append(";height: ").append("20pt"); // 字体高度
        sb.append(";z-index: ").append("-251654144");
        sb.append(";mso-wrap-edited: ").append("f");
        sb.append(";margin-top: ").append(styleTop);
        sb.append(";mso-position-horizontal-relative: ").append("margin");
        sb.append(";mso-position-vertical-relative: ").append("margin");
        sb.append(";mso-position-vertical: ").append("left");
        sb.append(";mso-position-horizontal: ").append("center");
        sb.append(";rotation: ").append(styleRotation);
        return sb.toString();
    }

    /**
     * 构建Shape的样式参数
     * @return
     */
    private static String getShapeStyle() {
        StringBuilder sb = new StringBuilder();
        sb.append("position: ").append("absolute"); // 文本path绘制的定位方式
        sb.append(";left: ").append("opt");
        sb.append(";width: ").append("500pt"); // 计算文本占用的长度（文本总个数*单字长度）
        sb.append(";height: ").append("150pt"); // 字体高度
        sb.append(";z-index: ").append("-251654144");
        sb.append(";mso-wrap-edited: ").append("f");
        sb.append(";margin-left: ").append("-50pt");
        sb.append(";margin-top: ").append("270pt");
        sb.append(";mso-position-horizontal-relative: ").append("margin");
        sb.append(";mso-position-vertical-relative: ").append("margin");
        sb.append(";mso-width-relative: ").append("page");
        sb.append(";mso-height-relative: ").append("page");
        sb.append(";rotation: ").append("-2949120f");
        return sb.toString();
    }

    /**
     * 替换word段落文本
     * @param docx
     * @param datamap
     */
    public static void replaceTextData(XWPFDocument docx, Map<String, Object> datamap,String imagePath){
        // 遍历所有的段落对象，将标记好的文本替换成我们想要的数据（这里无法处理表格，表格要另外处理）
        List<XWPFTable> tables = docx.getTables();
        // 替换表格数据
        if(!tables.isEmpty()){
            replaceTableData(docx, datamap,imagePath);

        }

        // 获取所有的段落
        List<XWPFParagraph> paragraphs = docx.getParagraphs();
        // 遍历所有的段落
        for (int i = 0; i < paragraphs.size(); i++) {
            // 获取该段所有的文本对象
            List<XWPFRun> runs = paragraphs.get(i).getRuns();
            for (int j = 0; j < runs.size(); j++) {
                XWPFRun run = runs.get(j);

                // 匹配内容，进行替换
                if(run != null && StrUtil.isNotEmpty(run.toString())){
                    Iterator<Map.Entry<String, Object>> iterator = datamap.entrySet().iterator();
                    while (iterator.hasNext()){
                        Map.Entry<String, Object> next = iterator.next();
                        if(run.toString().contains(next.getKey())){
                            run.setText(run.toString().replace(next.getKey(), datamap.get(next.getKey()).toString()), 0);
                        }
                    }

                }
            }
            if(StringUtils.isNotEmpty(imagePath)){
                // 获取段落标签
                for(CTBookmark bookmark :  paragraphs.get(i).getCTP().getBookmarkStartList()){

                    if(dataMap.containsKey(bookmark.getName())){
                        try{
                            docx.addPictureData(new FileInputStream(imagePath), XWPFDocument.PICTURE_TYPE_JPEG);
                            createPicture(docx.getAllPictures().size() - 1, 100, 45,paragraphs.get(i),docx);
                        }catch (Exception e){
                            logger.error("标签位置添加图片错误 Exception = {}",e);
                        }

                    }
                }
            }



        }
    }

    /**
     * 替换word表格内容
     * @param docx XWPFTable对象
     * @param datamap 数据
     */
    public static void replaceTableData(XWPFDocument docx, Map<String, Object> datamap,String imagePath){
        List<XWPFTable> tables = docx.getTables();
        for(XWPFTable table : tables) {
            // 获取这个表格所有的行
            List<XWPFTableRow> rows = table.getRows();
            // 遍历每一行
            for (XWPFTableRow xwpfTableRow : rows) {
                // 获取当前行所有的单元格
                List<XWPFTableCell> cells = xwpfTableRow.getTableCells();
                for (XWPFTableCell xwpfTableCell : cells) {
                    // 获取单元格中的文本段落
                    List<XWPFParagraph> para = xwpfTableCell.getParagraphs();
                    for (XWPFParagraph xwpfParagraph : para) {
                        List<XWPFRun> runs = xwpfParagraph.getRuns();
                        // 遍历文本段落，替换成我们想要的数据
                        for (int i = 0; i < runs.size(); i++) {
                            XWPFRun run = runs.get(i);
                            if (run != null && StrUtil.isNotEmpty(run.toString())) {
                                // 匹配内容，进行替换
                                Iterator<Map.Entry<String, Object>> iterator = datamap.entrySet().iterator();
                                while (iterator.hasNext()) {
                                    Map.Entry<String, Object> next = iterator.next();
                                    if (run.toString().contains(next.getKey())) {
                                        run.setText(run.toString().replace(next.getKey(), datamap.get(next.getKey()).toString()), 0);
                                    }

                                }
                            }
                        }
                        if(StringUtils.isNotEmpty(imagePath)){
                            // 获取段落标签
                            for(CTBookmark bookmark :  xwpfParagraph.getCTP().getBookmarkStartList()){

                                if(dataMap.containsKey(bookmark.getName())){
                                    try{
                                        docx.addPictureData(new FileInputStream(imagePath), XWPFDocument.PICTURE_TYPE_JPEG);
                                        createPicture(docx.getAllPictures().size() - 1, 100, 60,xwpfParagraph,docx);
                                    }catch (Exception e){
                                        logger.error("标签位置添加图片错误 Exception = {}",e);
                                    }

                                }
                            }
                        }
                    }
                }
            }
        }
    }


    /**
     * 读取 docx 文件中的所有书签（注意不支持书签嵌套，书签嵌套书签的情况只识别最外层书签）
     *
     * @param docx
     */
    public static List<Node> getBookmarksFromDocx(XWPFDocument docx) {
        Node bodyNode = docx.getDocument().getBody().getDomNode();

        // 递归读取 bookmarkStart 节点，返回bookmarkNode集合（<w:bookmarkStart w:id="1" w:name="书签名称"/>）
        List<Node> bookmarkNodeList = new ArrayList<>();
        getBookmarksFromNode(bodyNode, bookmarkNodeList);

        return bookmarkNodeList;
    }

    /**
     * 递归解析所有Node节点，将bookmark记录到集合中
     *
     * @param node
     * @param bookmarkNodeList
     */
    public static void getBookmarksFromNode(Node node, List<Node> bookmarkNodeList) {
        if (node.getNodeName().equals(BOOKMARK_START_TAG)) {
            bookmarkNodeList.add(node);
        } else if (node.getNodeName().equals(BOOKMARK_END_TAG) || node.getNodeName().equals(PARAGRAPH_PROPERTIES_NAME)) {
            // Nothing
        } else {
            NodeList childNodes = node.getChildNodes();
            for (int i = 0, j = childNodes.getLength(); i < j; i++) {
                Node childNode = childNodes.item(i);
                getBookmarksFromNode(childNode, bookmarkNodeList);
            }
        }
    }
    /**
     * @param id
     * @param width
     *            宽
     * @param height
     *            高
     * @param paragraph
     *            段落
     */
    public static void createPicture(int id, int width, int height,
                              XWPFParagraph paragraph,XWPFDocument docx) {
        final int EMU = 9525;
        width *= EMU;
        height *= EMU;
        String blipId = docx.getRelationId(docx.getAllPictures().get(id));
        CTInline inline = paragraph.createRun().getCTR().addNewDrawing()
                .addNewInline();
        String picXml = ""
                + "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">"
                + "   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
                + "      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
                + "         <pic:nvPicPr>" + "            <pic:cNvPr id=\""
                + id
                + "\" name=\"Generated\"/>"
                + "            <pic:cNvPicPr/>"
                + "         </pic:nvPicPr>"
                + "         <pic:blipFill>"
                + "            <a:blip r:embed=\""
                + blipId
                + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>"
                + "            <a:stretch>"
                + "               <a:fillRect/>"
                + "            </a:stretch>"
                + "         </pic:blipFill>"
                + "         <pic:spPr>"
                + "            <a:xfrm>"
                + "               <a:off x=\"0\" y=\"0\"/>"
                + "               <a:ext cx=\""
                + width
                + "\" cy=\""
                + height
                + "\"/>"
                + "            </a:xfrm>"
                + "            <a:prstGeom prst=\"rect\">"
                + "               <a:avLst/>"
                + "            </a:prstGeom>"
                + "         </pic:spPr>"
                + "      </pic:pic>"
                + "   </a:graphicData>" + "</a:graphic>";

        inline.addNewGraphic().addNewGraphicData();
        XmlToken xmlToken = null;
        try {
            xmlToken = XmlToken.Factory.parse(picXml);
        } catch (XmlException xe) {
            xe.printStackTrace();
        }
        inline.set(xmlToken);

        inline.setDistT(0);
        inline.setDistB(0);
        inline.setDistL(0);
        inline.setDistR(0);

        CTPositiveSize2D extent = inline.addNewExtent();
        extent.setCx(width);
        extent.setCy(height);

        CTNonVisualDrawingProps docPr = inline.addNewDocPr();
        docPr.setId(id);
        docPr.setName("签名");
        docPr.setDescr("签名");
    }


}

